
extern void outport_byte(unsigned short port, unsigned char data);
extern unsigned char inport_byte(unsigned short port);

/* lower level */

void increment_cursor(void);
void decrement_cursor(void);
void update_cursor(void);
void print_character(char c);
void line_break(void);
void erase_character(void);

/* higher level */

void print(char* msg);
void clear_screen(void);
void frame(void);
void keyboardISR(void);

/* Limpia la pantalla. Attr = fondo negro, letras verdes */

unsigned short int csr_x = 0, csr_y = 0;
unsigned char l_shift = 0, r_shift = 0, caps_lock = 0;

/* Escribe un mensaje (null terminated) en la posicion del cursor e incrementa el cursor */

void print(char* msg){
	while(*msg!=0){
		print_character(*msg);
		msg++;
	}
}

void clear_screen(void){
	char* videoMem = (char*) 0xB8000;
	unsigned int i = 0;
	while(i < (80*25*2)){
		videoMem[i] = 0;
		i++;
		videoMem[i] = 0x02;
		i++;
	}
	csr_x = 0;
	csr_y = 0;
	update_cursor();
}

void frame(void){
	clear_screen();
	char* videoMem = (char*) 0xB8000;
	unsigned int i = 0;
	while(i<(80*2)){
		videoMem[i] = '*';
		i+=2;
	}
	while(i<(80*24*2)){
		videoMem[i] = '*';
		i+=79*2;
		videoMem[i] = '*';
		i+=2;
	}
	while(i<(80*25*2)){
		videoMem[i] = '*';
		i+=2;
	}
	csr_x = 1;
	csr_y = 1;
	update_cursor();
}

void keyboardISR(void){

	unsigned char scan_code = inport_byte(0x60);
	char ascii_code;

	unsigned char character = 1;

	if((scan_code & 0x80) == 0){
		switch(scan_code){

		/* Linea 2 */

			case 0x01:
				// ESC
				character = 0;
				break;
			case 0x02:
				ascii_code = '1';
				break;
			case 0x03:
				ascii_code = '2';
				break;
			case 0x04:
				ascii_code = '3';
				break;
			case 0x05:
				ascii_code = '4';
				break;
			case 0x06:
				ascii_code = '5';
				break;
			case 0x07:
				ascii_code = '6';
				break;
			case 0x08:
				ascii_code = '7';
				break;
			case 0x09:
				ascii_code = '8';
				break;
			case 0x0a:
				ascii_code = '9';
				break;
			case 0x0b:
				ascii_code = '0';
				break;
			case 0x0e:
				// backspace
				erase_character();
				character = 0;
				break;

		/* Linea 3 */

			case 0x10:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'Q' : 'q';
				break;
			case 0x11:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'W' : 'w';
				break;
			case 0x12:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'E' : 'e';
				break;
			case 0x13:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'R' : 'r';
				break;
			case 0x14:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'T' : 't';
				break;
			case 0x15:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'Y' : 'y';
				break;
			case 0x16:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'U' : 'u';
				break;
			case 0x17:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'I' : 'i';
				break;
			case 0x18:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'O' : 'o';
				break;
			case 0x19:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'P' : 'p';
				break;
			case 0x1c:
				// enter
				line_break();
				character = 0;
				break;

			/* Linea 4 */

			case 0x1e:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'A' : 'a';
				break;
			case 0x1f:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'S' : 's';
				break;
			case 0x20:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'D' : 'd';
				break;
			case 0x21:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'F' : 'f';
				break;
			case 0x22:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'G' : 'g';
				break;
			case 0x23:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'H' : 'h';
				break;
			case 0x24:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'J' : 'j';
				break;
			case 0x25:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'K' : 'k';
				break;
			case 0x26:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'L' : 'l';
				break;
			/* Linea 5 */

			case 0x2a: 
				// left shift
				l_shift = 1;
				character = 0;
				break;
			case 0x2c:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'Z' : 'z';
				break;
			case 0x2d:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'X' : 'x';
				break;
			case 0x2e:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'C' : 'c';
				break;
			case 0x2f:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'V' : 'v';
				break;
			case 0x30:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'B' : 'b';
				break;
			case 0x31:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'N' : 'n';
				break;
			case 0x32:
				ascii_code = (caps_lock || l_shift || r_shift) ? 'M' : 'm';
				break;
			case 0x36:
				// right shift
				r_shift = 1;
				character = 0;
				break;

			/* Linea 6 */

			case 0x3a:
				// caps lock ?
				caps_lock = 1;
				character = 0;
				break;
			case 0x39:
				// spacebar
				ascii_code = 32;
				break;

			default:
				character = 0;
				break;
		}

		if(character==1) print_character(ascii_code);
	
	}else{
		switch(scan_code){
			case 0xaa:
				// left shift
				l_shift = 0;
				break;
			case 0xb6:
				// right shift
				r_shift = 0;
				break;
			case 0xba:
				// caps lock
				caps_lock = 0;
				break;
			default: break;
		}
	}

}

void print_character(char c){
	char* videoMem = (char*) 0xB8000;
	unsigned int i = 2*(csr_y*80 + csr_x);
	videoMem[i] = c;
	increment_cursor();
}

void increment_cursor(void){
	csr_x++;
	if(csr_x==80){
		csr_x = 0;
		csr_y++;
	}
	update_cursor();
}

void decrement_cursor(void){
	if(csr_x==0){
		csr_x = 80;
		csr_y--;
	}else{
		csr_x--;
	}
	update_cursor();
}

void update_cursor(void){
	unsigned short int c = csr_y*80 + csr_x;
	outport_byte(0x3D4, 14);
    outport_byte(0x3D5, c >> 8);
    outport_byte(0x3D4, 15);
    outport_byte(0x3D5, c);
}

void line_break(void){
	csr_y++;
	csr_x = 0;
	update_cursor();
}

void erase_character(void){
	decrement_cursor();
	print_character(0);
	decrement_cursor();
}
